from datetime import datetime
from beancount.core.data import Transaction, Posting, Amount
from beancount.core.number import Decimal
from beancount.parser import printer
from typing import List, Tuple
from beancount.loader import load_file
from beancount.core import getters
import os
import json

# 设置默认的beancount文件路径
DEFAULT_BEANCOUNT_FILE = os.path.join(os.path.dirname(__file__), "ledger.beancount")

def validate_accounts(postings: List[Tuple[str, float, str]], file_path: str = None) -> tuple:
    """
    验证交易中的科目是否在账套中存在
    
    Args:
        postings: 会计分录列表
        file_path: 账套文件路径
    Returns:
        tuple: (是否验证通过, 错误信息, 现有账户列表)
    """
    if file_path is None:
        file_path = DEFAULT_BEANCOUNT_FILE
        
    try:
        # 加载账套文件
        entries, errors, options = load_file(file_path)
        if errors:
            return False, f"账套文件存在错误: {errors}", []
            
        # 获取所有已定义的账户
        accounts = getters.get_accounts(entries)
        
        # 验证每个科目是否存在
        missing_accounts = []
        for account, _, currency in postings:
            if account not in accounts:
                missing_accounts.append((account, currency))
        
        if missing_accounts:
            missing_info = ", ".join([f"{account}({currency})" for account, currency in missing_accounts])
            return False, f"科目不存在: {missing_info}", list(accounts)
        
        return True, "", list(accounts)
        
    except Exception as e:
        error_message = f"验证科目失败: {e}"
        return False, error_message, []

def create_transaction(
    date: str,
    description: str,
    postings: List[Tuple[str, float, str]],
    file_path: str = None,
    narration: str = "",
    tags: set = None,
    meta: dict = None
) -> dict:
    """
    创建会计凭证（交易记录）并写入文件
    
    Args:
        date: 交易日期，格式 YYYY-MM-DD
        description: 交易描述
        postings: 会计分录列表
        file_path: 账套文件路径
        narration: 详细说明
        tags: 标签集合
        meta: 元数据字典
    Returns:
        dict: 创建结果信息
    """
    if file_path is None:
        file_path = DEFAULT_BEANCOUNT_FILE
        
    # 验证科目是否存在
    is_valid, error_message, existing_accounts = validate_accounts(postings, file_path)
    if not is_valid:
        # 构建详细的错误信息，包括缺失的账户和现有账户列表
        missing_accounts = []
        for account, _, currency in postings:
            if account not in existing_accounts:
                missing_accounts.append((account, currency))
        
        return {
            "success": False,
            "error": error_message,
            "existing_accounts": existing_accounts,
            "missing_accounts": missing_accounts
        }
        
    try:
        if date is None:
            date_obj = datetime.now().date()
        else:
            date_obj = datetime.strptime(date, "%Y-%m-%d").date()
        
        # 创建分录列表
        final_postings = []
        for account, amount, currency in postings:
            posting = Posting(
                account=account,
                units=Amount(Decimal(str(amount)), currency),
                cost=None,
                price=None,
                flag=None,
                meta=None
            )
            final_postings.append(posting)
        
        # 创建交易记录
        transaction = Transaction(
            meta=meta or {},
            date=date_obj,
            flag="*",
            payee=None,
            narration=description,
            tags=tags or set(),
            links=set(),
            postings=final_postings
        )
        
        # 将交易记录写入文件
        with open(file_path, 'a', encoding='utf-8') as f:
            f.write('\n' + printer.format_entry(transaction) + '\n')
        
        return {
            "success": True,
            "message": "交易记录已创建",
            "transaction": {
                "date": date,
                "description": description,
                "narration": narration,
                "postings": postings,
                "tags": list(tags) if tags else []
            }
        }
    except Exception as e:
        return {
            "success": False,
            "error": str(e)
        }